package com.koron.operation.classes.schedule.excelHandler;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.write.handler.RowWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import com.koron.bean.system.staff.StaffBean;
import com.koron.common.core.business.common.mapper.StaffMapper;
import com.koron.operation.classes.info.bean.po.ClassesItem;
import com.koron.operation.classes.schedule.bean.dto.ClassesShiftDTO;
import com.koron.operation.classes.schedule.bean.dto.ScheduleDTO;
import com.koron.operation.classes.schedule.bean.po.ClassesStaff;
import lombok.SneakyThrows;
import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.koron.ebs.mybatis.SessionFactory;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 * 排班班次下载模板时添加批注
 * @author jzp
 * @since 2021/10/22
 */
public class ScheduleWriteHandler implements RowWriteHandler {

    private List<ClassesItem> items;

    private List<ClassesShiftDTO> classesShiftDTOS;

    private Map<String, String> staffMap = new HashMap<>();

    private Drawing<?> drawingPatriarch;

    private XSSFClientAnchor xssfClientAnchor = new XSSFClientAnchor(0, 0, 0, 0, 0, 0, 0, 0);

    private Integer maxSort = 0;
    private String staffType;

    private boolean hasError = false;

    private CellStyle readCellStyle;

    public ScheduleWriteHandler(ScheduleDTO scheduleDTO) {
        this.items = scheduleDTO.getItems();
        this.classesShiftDTOS = scheduleDTO.toClassesShiftDTOs();
        this.staffType = scheduleDTO.getStaffType();
    }

    @SneakyThrows
    @Override
    public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer rowIndex, Boolean isHead) {
        if (readCellStyle == null) {
            drawingPatriarch = row.getSheet().createDrawingPatriarch();
            CellStyle cellStyle = writeSheetHolder.getParentWriteWorkbookHolder().getWorkbook().createCellStyle();
            cellStyle.setFillForegroundColor(IndexedColors.RED.getIndex());
            // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND
            cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
            this.readCellStyle = cellStyle;
        }
        // 如果是头部，就给头部加上备注信息
        if (isHead) {
            // 给第一列，打上备注信息 值班开始时间
            Cell cell = row.getCell(0);
            drawComment(cell, row.getRowNum(), "值班开始时间为首班开始日期，日期格式为yyyy/MM/dd，其他班次的日期时间会自动生成", false);
        } else {
            Iterator<Cell> cellIterator = row.cellIterator();
            // 第一列，日期列
            Cell dateCell = cellIterator.next();
            // 获取数据库中最后一条数据的日期，用以校检日期
            ClassesShiftDTO finalClassesShiftDTO = CollectionUtils.isEmpty(classesShiftDTOS) ? null : classesShiftDTOS.get(classesShiftDTOS.size() - 1);
            if (finalClassesShiftDTO != null) {
                validDate(dateCell, row.getRowNum(), finalClassesShiftDTO.getEndTime());
                maxSort = finalClassesShiftDTO.getSort();
            }
            Date endTime = null;
            // 遍历这一行的人员列
            while (cellIterator.hasNext()) {
                Cell cell = cellIterator.next();
                ClassesItem classesItem;
                // 如果是0值班长+值班员或3OnCall
                if (StrUtil.equalsAny(staffType, "0", "3")) {
                    classesItem = items.get((cell.getColumnIndex() - 1) / 2);
                } else {
                    classesItem = items.get(cell.getColumnIndex() - 1);
                }
                ClassesShiftDTO classesShiftDTO = new ClassesShiftDTO();
                classesShiftDTO.setName(classesItem.getName());
                classesShiftDTO.setBlanked(classesItem.getBlanked());
                classesShiftDTO.setStatus(0);
                // 设置开始时间和结束时间
                String date;
                if (endTime != null) {
                    date = DateFormatUtils.format(endTime, "yyyy-MM-dd HH:mm");
                } else {
                    date = dateCell.getStringCellValue() + " " + DateFormatUtils.format(classesItem.getStartTime(), "HH:mm");
                }
                classesShiftDTO.setStartTime(DateUtils.parseDate(date, "yyyy-MM-dd HH:mm", "yyyy/MM/dd HH:mm"));
                endTime = classesShiftDTO.computeEndTime(classesItem.getDuration());

                List<ClassesStaff> classesStaffs = new ArrayList<>();
                // 设置人员
                // 如果是空白帮，不允许输入人员信息
                if (classesShiftDTO.getBlanked() == 1) {
                    String staffString = cell.getStringCellValue();
                    if (!StringUtils.isEmpty(staffString)) {
                        drawComment(cell, rowIndex, "空白班不能有人", true);
                    }
                    //如果是值班长+值班员，则往后跳一格
                    if ("0".equals(staffType)) {
                        cell = cellIterator.next();
                        staffString = cell.getStringCellValue();
                        if (!StringUtils.isEmpty(staffString)) {
                            drawComment(cell, row.getRowNum(), "空白班不能有人", true);
                        }
                    }
                } else {
                    switch (staffType) {
                        // 如果是0 值班长+值班员
                        case "0":
                            // 值班长
                            classesStaffs = validStaffs(cell, row.getRowNum(), "0");
                            if (classesStaffs.size() > 1) {
                                drawComment(cell, rowIndex, "不能有多个值班长", true);
                            }
                            // 值班员
                            cell = cellIterator.next();
                            classesStaffs.addAll(validStaffs(cell, row.getRowNum(), "2"));
                            if (CollUtil.isEmpty(classesStaffs)) {
                                drawComment(cell, rowIndex, "值班长和值班员不能同时为空", true);
                            }
                            break;
                        // 如果是1 值守
                        case "1":
                            classesStaffs = validStaffs(cell, row.getRowNum(), "1");
                            if (CollUtil.isEmpty(classesStaffs)) {
                                drawComment(cell, rowIndex, "值守不能为空", true);
                            }
                            break;
                        // 如果是2 值班员
                        case "2":
                            classesStaffs = validStaffs(cell, row.getRowNum(), "2");
                            if (CollUtil.isEmpty(classesStaffs)) {
                                drawComment(cell, rowIndex, "值班员不能为空", true);
                            }
                            break;
                        // 如果是3 OnCall
                        case "3":
                            // 值班领导
                            classesStaffs = validStaffs(cell, row.getRowNum(), "3");
                            if (classesStaffs.size() > 1) {
                                drawComment(cell, rowIndex, "不能有多个值班领导", true);
                            }
                            // 值班员
                            cell = cellIterator.next();
                            classesStaffs.addAll(validStaffs(cell, row.getRowNum(), "4"));
                            if (CollUtil.isEmpty(classesStaffs)) {
                                drawComment(cell, rowIndex, "值班领导和值班人员不能同时为空", true);
                            }
                            break;
                    }
                }
                classesShiftDTO.setStaffs(classesStaffs);
                classesShiftDTO.setSort(++maxSort);
                classesShiftDTOS.add(classesShiftDTO);
            }
        }

    }

    public List<ClassesShiftDTO> getClassesShiftDTOS() {
        return classesShiftDTOS;
    }

    public boolean isHasError() {
        return hasError;
    }

    /**
     * 给单元格打上备注信息
     */
    private void drawComment(Cell cell, Integer rowIndex, String comment, boolean isStyle) {
        int columnIndex = cell.getColumnIndex();
        Comment cellComment = cell.getCellComment();
        if (cellComment == null) {
            xssfClientAnchor.setCol1(columnIndex);
            xssfClientAnchor.setCol2(columnIndex + 2);
            xssfClientAnchor.setRow1(rowIndex);
            xssfClientAnchor.setRow2(rowIndex + 2);
            cellComment = drawingPatriarch.createCellComment(xssfClientAnchor);
        } else if (cellComment.getString() != null){
            comment = cellComment.getString().getString() + "；"+ comment;
        }

        cellComment.setString(new XSSFRichTextString(comment));
        cell.setCellComment(cellComment);
        if (isStyle) {
            cell.setCellStyle(this.readCellStyle);
        }

        // 非头部
        if (rowIndex > 1) {
            hasError = true;
        }
    }

    /**
     * @param cell 当前单元格，从这里获取当前时间
     * @param rowIndex 当前第几行
     * @param endTime 上一行的结束时间
     */
    private void validDate(Cell cell, Integer rowIndex, Date endTime) {
        String dateStr = cell.getStringCellValue();
        DateTime dateTime;
        if (StrUtil.isEmpty(dateStr)) {
            Date dateCellValue = cell.getDateCellValue();
            Assert.notNull(dateCellValue, "日期不能为null");
            dateTime = new DateTime(dateCellValue);
        } else {
            dateTime = DateUtil.parseDate(dateStr);
        }
        String formatEndTime = DateUtil.format(endTime, "yyyy-MM-dd");

        if (dateTime.before(DateUtil.parseDate(formatEndTime))) {
            drawComment(cell, rowIndex, "上一班次的结束日期为" + formatEndTime + "，不能小于这个日期", true);
        }
        cell.setCellValue(dateTime.toString("yyyy-MM-dd"));
    }

    public List<ClassesStaff> validStaffs(Cell cell, Integer rowIndex, String dutyType) {
        String staffString = cell.getStringCellValue();
        List<ClassesStaff> staffs = new ArrayList<>();
        if (StringUtils.isEmpty(staffString)) {
            return staffs;
        }
        String[] staffArr = staffString.split("[,，、]");
        for (String s : staffArr) {
            if (StrUtil.isEmpty(s)) {
                continue;
            }
            ClassesStaff staff2 = getStaff(cell, rowIndex, s);
            staff2.setType(dutyType);
            staffs.add(staff2);
        }
        return staffs;
    }

    @Deprecated
    public List<ClassesStaff> validStaffs(Cell cell, Integer rowIndex) {
        String staffString = cell.getStringCellValue();
        List<ClassesStaff> staffs = new ArrayList<>();
        if (StringUtils.isEmpty(staffString)) {
            return staffs;
        }
        String[] staffArr = staffString.split(",");
        switch (staffType) {
            // 值班长+值班员
            case "0":
                String[] split = staffString.split("\\|");
                if (split.length > 2) {
                    drawComment(cell, rowIndex, "只能有一个值班员", true);
                    break;
                } else if (split.length == 2 || (split.length == 1 && staffString.contains("|"))) {
                    if (split[0].contains(",")) {
                        drawComment(cell, rowIndex, "不能有多个值班长", true);
                        break;
                    }
                    // 设置值班长
                    ClassesStaff staff1 = getStaff(cell, rowIndex, split[0]);
                    staff1.setType("0");
                    staffs.add(staff1);
                    // 设置值班员
                    if (split.length == 1){
                        break;
                    }
                    staffArr = split[1].split(",");
                }
            // 值班员
            case "2":
                for (String s : staffArr) {
                    ClassesStaff staff2 = getStaff(cell, rowIndex, s);
                    staff2.setType("2");
                    staffs.add(staff2);
                }
                break;
            // 值守
            case "1":
                for (String s : staffArr) {
                    ClassesStaff staff2 = getStaff(cell, rowIndex, s);
                    staff2.setType("1");
                    staffs.add(staff2);
                }
                break;
        }
        return staffs;
    }

    public ClassesStaff getStaff(Cell cell, Integer rowIndex, String name) {
        ClassesStaff classesStaff = new ClassesStaff();
        String account = getStaffAccountByName(name);
        if (account == null) {
            drawComment(cell, rowIndex, "用户名："+name+"不存在", true);
        } else if ("".equals(account)) {
            drawComment(cell, rowIndex, "该用户名：" + name + "在数据库中重复，请在用户名后加上|用户账号，如：张三|zhangsan", true);
        } else {
            classesStaff.setStaffBy(account);
            classesStaff.setStaffName(name);
            if (name.contains("|")) {
                classesStaff.setStaffName(name.split("\\|")[0]);
            }
            return classesStaff;
        }
        return classesStaff;
    }

    public String getStaffAccountByName(String name) {
        if (staffMap.containsKey(name)) {
            return staffMap.get(name);
        }
        StaffBean staffBean = new StaffBean();
        staffBean.setName(name);
        String[] split = name.split("\\|", 2);
        if (split.length == 2) {
            staffBean.setName(split[0]);
            staffBean.setAccount(split[1]);
        }
        try (SessionFactory factory = new SessionFactory()) {
            StaffMapper mapper = factory.getMapper(StaffMapper.class);
            List<StaffBean> staffBeans = mapper.selectList(staffBean);
            // 用户不存在
            if (staffBeans == null || staffBeans.size() == 0) {
                staffMap.put(name, null);
                return null;
            } else if (staffBeans.size() > 1) {
                // 同名用户
                staffMap.put(name, "");
                return "";
            }
            staffMap.put(name, staffBeans.get(0).getAccount());
            return staffBeans.get(0).getAccount();
        }
    }

}
